package com.meida.module.bus.provider.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.meida.common.base.entity.EntityMap;
import com.meida.common.base.service.BaseAppAccountService;
import com.meida.common.base.utils.FlymeUtils;
import com.meida.common.enums.AccountTypeEnum;
import com.meida.common.mybatis.base.service.impl.BaseServiceImpl;
import com.meida.common.mybatis.model.ResultBody;
import com.meida.common.mybatis.query.CriteriaQuery;
import com.meida.common.security.OpenHelper;
import com.meida.common.utils.ApiAssert;
import com.meida.common.utils.RedisUtils;
import com.meida.module.bus.client.entity.AppUserExtra;
import com.meida.module.bus.client.entity.PayBalanceLogExtra;
import com.meida.module.bus.client.enums.ExamineStatusEnum;
import com.meida.module.bus.client.enums.OrderTypeEnum;
import com.meida.module.bus.provider.service.AppUserExtraService;
import com.meida.module.bus.provider.service.BusOrderInfoService;
import com.meida.module.bus.provider.service.BusPayBalanceLogService;
import com.meida.module.bus.provider.service.BusPayBalanceService;
import com.meida.module.pay.client.entity.PayBalance;
import com.meida.module.pay.client.entity.PayBalanceLog;
import com.meida.module.pay.client.entity.PayConfig;
import com.meida.module.pay.client.vo.PayRequest;
import com.meida.module.pay.provider.mapper.PayBalanceMapper;
import com.meida.module.pay.provider.service.PayBalanceLogService;
import com.meida.module.pay.provider.service.PayConfigService;
import org.joda.time.DateTime;
import org.joda.time.Years;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;

/**
 * 余额表 实现类
 *
 * @author flyme
 * @date 2019-12-16
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class BusPayBalanceServiceImpl extends BaseServiceImpl<PayBalanceMapper, PayBalance> implements BusPayBalanceService {

    @Resource
    private PayBalanceLogService payBalanceLogService;

    @Resource
    private BusPayBalanceLogService busPayBalanceLogService;

    @Autowired(required = false)
    private BaseAppAccountService baseAppAccountService;

    @Autowired(required = false)
    private PayConfigService payConfigService;

    @Resource
    private RedisUtils redisUtils;

    @Autowired
    private RebateLogicService rebateLogicService;

    @Resource
    private AppUserExtraService appUserService;

    @Resource
    private BusOrderInfoService busOrderInfoService;

    @Override
    public ResultBody beforePageList(CriteriaQuery<PayBalance> cq, PayBalance balance, EntityMap requestMap) {
        cq.select("user.userName", "user.nickName", "user.mobile");
        cq.select(PayBalance.class, "*");
        cq.eq("user.userName", requestMap.get("userName"));
        cq.eq("user.mobile", requestMap.get("mobile"));
        cq.createJoinAppUser();
        return super.basePageList(cq);
    }

    @Override
    public PayBalance getByUserIdAndUserType(Long userId, String userType) {
        return getByUserIdAndUserType(userId, userType, true);
    }

    @Override
    public boolean addBalance(Long userId, String userType, BigDecimal amount, String outTradeNo, Integer type, Integer status, Long busId, String title, String body, Long sourceUserId) {
        PayBalance balance = getByUserIdAndUserType(userId, userType);
        //10消费 20返佣,30分红 40加权分红 21返佣提现 31分红提现 41 加权分红提现 50退回
        //判断是否完成任务否则不分红
        Integer popularize = redisUtils.getInteger("POPULARIZE");
        DateTime d1 = new DateTime(appUserService.getById(userId).getCreateTime());
        DateTime d2 = new DateTime(new Date());
        int years = Years.yearsBetween(d1, d2).getYears();
        if (years > 0) {
            Long count = busOrderInfoService.getCountByYearAndUserId(DateUtil.thisYear(), userId);
            if (count < popularize) {
                return false;
            }
        }
        if (OrderTypeEnum.OrderType20.getCode().equals(type)) {
            balance.setCommAmount(balance.getCommAmount().add(amount));
        }
        if (OrderTypeEnum.OrderType21.getCode().equals(type)) {
            balance.setCommAmount(balance.getCommAmount().subtract(amount));
        }
        if (OrderTypeEnum.OrderType30.getCode().equals(type)) {
            balance.setAmount(balance.getAmount().add(amount));
        }
        if (OrderTypeEnum.OrderType31.getCode().equals(type)) {
            balance.setAmount(balance.getAmount().subtract(amount));
        }
        if (OrderTypeEnum.OrderType40.getCode().equals(type)) {
            balance.setBindAmount(balance.getBindAmount().add(amount));
        }
        if (OrderTypeEnum.OrderType41.getCode().equals(type)) {
            balance.setBindAmount(balance.getBindAmount().subtract(amount));
        }
        boolean update = balance.updateById();
        //余额变动有乐观锁，高并发的时候会更新失败。
        if (update) {
            PayBalanceLog balanceLog = new PayBalanceLog();
            balanceLog.setUserId(userId).setUserType(userType).setBalanceId(balance.getBalanceId()).setAmount(amount).setTradeNo(outTradeNo).setLogType(type).setStatus(status).setBusId(busId).setTitle(title).setBody(body).setSourceUserId(sourceUserId);
            payBalanceLogService.save(balanceLog);
        }
        return update;
    }


    @Override
    public boolean subtractBalance(PayRequest payRequest) {
        Long userId = payRequest.getUserId();
        String userType = payRequest.getUserType();
        PayBalance balance = getByUserIdAndUserType(userId, userType, false);
        ApiAssert.isNotEmpty("账户余额不足!", balance);
        Object appScanPay = payRequest.getAppScanPay();
        if (FlymeUtils.isEmpty(appScanPay)) {
            Boolean isCheckPayPwd = payRequest.getIsCheckPayPwd();
            if (isCheckPayPwd) {
                String dbPayPwd = balance.getPaypwd();
                ApiAssert.isNotEmpty("请设置支付密码", dbPayPwd);
                //支付密码
                String payPwd = payRequest.getPayPwd();
                ApiAssert.eq("支付密码不正确", payPwd, dbPayPwd);
            }
        }
        Boolean useBindAmount = payRequest.getUseBindAmount();
        //账户余额
        BigDecimal balanceAmount = FlymeUtils.getBigDecimal(balance.getAmount(), "0");
        //支付金额
        BigDecimal payAmount = payRequest.getPayAmount();
        if (FlymeUtils.isNotEmpty(useBindAmount) && useBindAmount) {
            //绑定金使用模式1.混用,2仅使用绑定金
            Integer useBindAmountType = payRequest.getUseBindAmountType();
            //绑定金余额
            BigDecimal bindAmount = FlymeUtils.getBigDecimal(balance.getBindAmount(), "0");
            if (useBindAmountType.equals(1)) {
                //账户总金额
                BigDecimal totalAmount = balanceAmount.add(bindAmount);
                if (FlymeUtils.lt(totalAmount, payAmount)) {
                    ApiAssert.failure("账户余额不足!");
                }
                if (FlymeUtils.gezero(bindAmount)) {
                    //绑定金够支付的情况
                    if (FlymeUtils.ge(bindAmount, payAmount)) {
                        balance.setBindAmount(bindAmount.subtract(payAmount));
                    }
                    //绑定金不够支付的情况
                    if (FlymeUtils.lt(bindAmount, payAmount)) {
                        BigDecimal surplusAmount = payAmount.subtract(bindAmount);
                        balance.setBindAmount(new BigDecimal("0"));
                        balance.setAmount(balanceAmount.subtract(surplusAmount));
                    }
                }
            }
            if (useBindAmountType.equals(2)) {
                if (FlymeUtils.lt(bindAmount, payAmount)) {
                    ApiAssert.failure("账户余额不足!");
                }
                balance.setBindAmount(bindAmount.subtract(payAmount));
            }
        } else {
            balance.setAmount(balanceAmount.subtract(payAmount));
            if (FlymeUtils.lt(balanceAmount, payAmount)) {
                ApiAssert.failure("余额不足");
            }
            balance.setAmount(balanceAmount.subtract(payAmount));
        }
        boolean update = balance.updateById();
        //余额变动有乐观锁，高并发的时候会更新失败。
        if (update) {
            PayBalanceLog balanceLog = new PayBalanceLog();
            balanceLog.setUserId(userId).
                    setUserType(userType).
                    setBalanceId(balance.getBalanceId()).
                    setAmount(new BigDecimal("-" + payAmount)).
                    setTradeNo(payRequest.getOutTradeNo()).
                    setLogType(payRequest.getLogType()).
                    setBusId(payRequest.getBusId()).
                    setTitle(payRequest.getTitle()).
                    setBody(payRequest.getBody()).
                    setStatus(payRequest.getStatus());
            balanceLog.insert();
        }
        return update;
    }


    @Override
    public PayBalance addUserBalance(Long userId) {
        PayBalance balance = getByUserId(userId);
        if (FlymeUtils.isEmpty(balance)) {
            balance = new PayBalance();
            balance.setUserId(userId);
            balance.setBalanceId(userId);
            balance.setVersion(100L);
            balance.setCompanyId(OpenHelper.getCompanyId());
            balance.setAmount(new BigDecimal("0"));
            balance.setBindAmount(new BigDecimal("0"));
            balance.setCommAmount(new BigDecimal("0"));
            save(balance);
        }
        return balance;
    }

    @Override
    public ResultBody setPayPwd(String payPwd, String smsCode) {
        Long userId = OpenHelper.getUserId();
        if (FlymeUtils.isNotEmpty(baseAppAccountService)) {
            EntityMap entityMap = baseAppAccountService.getAppAccount(userId, AccountTypeEnum.MOBILE.getCode());
            String account = entityMap.get("account");
            //校验验证码
            redisUtils.validSmsCode(account, smsCode);
        }
        // String encodePassword = passwordEncoder.encode(paypwd);
        PayBalance balance = addUserBalance(userId);
        balance.setPaypwd(payPwd);
        updateById(balance);
        return ResultBody.msg("设置成功");
    }


    @Override
    public PayBalance getByUserId(Long userId) {
        return getByProperty("userId", userId);
    }

    @Override
    public Boolean addBindAmount(Long userId, Long sourceUserId, BigDecimal amount) {
        return addBindAmount(userId, sourceUserId, amount, 1, "邀请奖励", "收益");
    }

    @Override
    public Boolean addBindAmount(Long userId, Long sourceUserId, BigDecimal amount, Integer logType, String title, String body) {
        PayBalance balance = getByUserId(userId);
        if (FlymeUtils.isEmpty(balance)) {
            //当推荐人账户不存在时创建账户
            balance = new PayBalance();
            balance.setUserId(userId);
            balance.setBindAmount(amount);
            balance.setVersion(100L);
            balance.setAmount(new BigDecimal("0"));
            save(balance);
        } else {
            BigDecimal bindAmount = balance.getBindAmount();
            balance.setBindAmount(bindAmount.add(amount));
            updateById(balance);
        }
        return payBalanceLogService.addBalanceLog(balance.getBalanceId(), userId, amount, body, title, logType, sourceUserId);
    }

    @Override
    public ResultBody bindCashAccount(Integer bindCashType, String cashAccount) {
        ApiAssert.allNotNull("缺少必要参数", bindCashType, cashAccount);
        Long userId = OpenHelper.getUserId();
        PayBalance payBalance = addUserBalance(userId);
        if (bindCashType == 1) {
            payBalance.setWxOpenId(cashAccount);
        }
        if (bindCashType == 2) {
            payBalance.setAliPayAccount(cashAccount);
        }
        updateById(payBalance);
        return ResultBody.ok("绑定成功");
    }

    @Override
    public ResultBody getAliUserInfoByAuthCode(String authCode, Long payId) {
        if (FlymeUtils.isEmpty(authCode)) {
            return ResultBody.failed("auth_code不能为空");
        }
        if (FlymeUtils.isEmpty(payId)) {
            return ResultBody.failed("payId不能为空");
        }
        PayConfig payConfig = payConfigService.getById(payId);
        String appId = payConfig.getAppId();
        String privateKey = payConfig.getPrivateKey();
        String publicKey = payConfig.getPublicKey();
        String inputCharset = payConfig.getInputCharset();
        String signType = payConfig.getSignType();
        AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", appId, privateKey, "json", inputCharset, publicKey, signType);
        AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
        request.setCode(authCode);
        request.setGrantType("authorization_code");
        try {
            AlipaySystemOauthTokenResponse oauthTokenResponse = alipayClient.execute(request);
            return ResultBody.ok(oauthTokenResponse);
        } catch (AlipayApiException e) {
            //处理异常
            e.printStackTrace();
        }
        return ResultBody.failed("获取失败");
    }

    /**
     * 创建用户账户
     *
     * @param userId
     * @param userType
     * @param isReturn 是否返回账户对象
     * @return
     */
    private PayBalance getByUserIdAndUserType(Long userId, String userType, Boolean isReturn) {
        CriteriaQuery<PayBalance> cq = new CriteriaQuery(PayBalance.class);
        PayBalance balance = getOne(cq.eq("userId", userId).eq("userType", userType));
        //如果用户的余额为空，重新插入余额。
        if (FlymeUtils.isEmpty(balance)) {
            PayBalance addPayBalance = new PayBalance();
            addPayBalance.setUserId(userId).setUserType(userType).setAmount(new BigDecimal(0)).setBindAmount(new BigDecimal(0)).setCommAmount(new BigDecimal(0)).setVersion(new Long(1));
            addPayBalance.insert();
            if (isReturn) {
                balance = addPayBalance;
            }
        }
        return balance;
    }


    /**
     * description: 申请提现
     * date: 2022年-06月-27日 11:07
     * author: ldd
     *
     * @param userId
     * @param money
     * @return com.meida.common.mybatis.model.ResultBody
     */
    @Override
    public ResultBody withdrawals(Long userId, BigDecimal money, Integer logType) {
        PayBalance balance = getByUserId(userId);
        PayBalanceLogExtra balanceLog = new PayBalanceLogExtra();

        JSONObject object = JSON.parseObject(redisUtils.getConfig("CHARGES_CONFIG"));
        BigDecimal charges = object.getBigDecimal("charges");
        BigDecimal baseline = object.getBigDecimal("baseline");
        if (money.compareTo(baseline) == -1) {
            return ResultBody.failed("单笔提现最小金额为" + baseline + "元");
        }
        //10消费 20返佣,30分红 40加权分红 21返佣提现 31分红提现 41 加权分红提现 50退回
        if (OrderTypeEnum.OrderType21.getCode().equals(logType)) {
            if (balance.getCommAmount().compareTo(money) == -1) {
                return ResultBody.failed("余额不足");
            }
            balance.setCommAmount(balance.getCommAmount().subtract(money));
        }

        if (OrderTypeEnum.OrderType31.getCode().equals(logType)) {
            if (balance.getAmount().compareTo(money) == -1) {
                return ResultBody.failed("余额不足");
            }
            balance.setAmount(balance.getAmount().subtract(money));
        }

        if (OrderTypeEnum.OrderType41.getCode().equals(logType)) {
            if (balance.getBindAmount().compareTo(money) == -1) {
                return ResultBody.failed("余额不足");
            }
            balance.setBindAmount(balance.getBindAmount().subtract(money));
        }
        balance.updateById();
        BigDecimal chargesMoney = money.multiply(charges);
        balanceLog.setRebate(chargesMoney).setUserId(userId).setUserType(balance.getUserType()).setBalanceId(balance.getBalanceId()).setAmount(money.subtract(chargesMoney)).setLogType(logType).setStatus(ExamineStatusEnum.ing.getValue()).setBusId(IdWorker.getId()).setTitle(OrderTypeEnum.getByValue(logType).getDesc());
        busPayBalanceLogService.save(balanceLog);
        return ResultBody.ok("申请提现成功");
    }

    @Transactional
    @Override
    public ResultBody withdrawalsApproval(Long balanceLogId, Integer status) {
        PayBalanceLogExtra log = busPayBalanceLogService.getById(balanceLogId);
        if (ObjectUtil.isNull(log)) {
            return ResultBody.failed("提现记录不存在");
        }
        log.setStatus(status);
        boolean b = log.updateById();
        if (ExamineStatusEnum.finsh.getValue().equals(status)) {
            AppUserExtra appUser = appUserService.getById(log.getUserId());
            rebateLogicService.calculateWeighting(log.getRebate(), appUser.getFullName(), log.getUserId(), log.getBalanceLogId());
        } else if (ExamineStatusEnum.fail.getValue().equals(status)) {
            PayBalance balance = getByUserId(log.getUserId());
            //10消费 20返佣,30分红 40加权分红 21返佣提现 31分红提现 41 加权分红提现 50退回
            if (OrderTypeEnum.OrderType21.getCode().equals(log.getLogType())) {
                balance.setCommAmount(balance.getCommAmount().add(log.getRebate()).add(log.getAmount()));
            }
            if (OrderTypeEnum.OrderType31.getCode().equals(log.getLogType())) {
                balance.setAmount(balance.getCommAmount().add(log.getRebate()).add(log.getAmount()));
            }
            if (OrderTypeEnum.OrderType41.getCode().equals(log.getLogType())) {
                balance.setBindAmount(balance.getCommAmount().add(log.getRebate()).add(log.getAmount()));
            }
            balance.updateById();
        }
        return ResultBody.ok("审批完成");
    }

    @Override
    public ResultBody withdrawalsPage(Map map) {
        return payBalanceLogService.pageList(map);
    }
}
